package com.lpf.test01;

import cn.hutool.json.JSONUtil;
import com.lpf.flowable.FlowableUIApplication;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import javax.annotation.Resource;
import java.util.*;

@RunWith(SpringRunner.class)
//这里的Application是springboot的启动类名
@SpringBootTest(classes = FlowableUIApplication.class)
@WebAppConfiguration
public class 简单流程测试 {

    // 流程引擎
    @Resource
    private ProcessEngine processEngine;
    // 用户以及组管理服务
    @Resource
    private IdentityService identityService;
    // 模型服务
    @Resource
    private ModelService modelService;
    // 部署服务
    @Resource
    private RepositoryService repositoryService;
    // 流程实例服务
    @Resource
    private RuntimeService runtimeService;
    // 流程节点任务服务
    @Resource
    private TaskService taskService;
    // 历史数据服务
    @Resource
    private HistoryService historyService;

    /**
     * 流程部署
     * <p>
     * 流程ID，来自 ACT_DE_MODEL
     */
    @Test
    public void deploy() {

        String modelId = "4a1de526-c329-11ea-aa69-5afb84df620e";

        // 根据模型 ID 获取模型
        Model modelData = modelService.getModel(modelId);

        byte[] bytes = modelService.getBpmnXML(modelData);
        if (bytes == null) {
            System.out.println("模型数据为空，请先设计流程并成功保存，再进行发布");
        }

        BpmnModel model = modelService.getBpmnModel(modelData);
        if (model.getProcesses().size() == 0) {
            System.out.println("数据模型不符要求，请至少设计一条主线流程");
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName() + ".bpmn20.xml";

        // 部署流程
        Deployment deploy = repositoryService.createDeployment()
                .name(modelData.getName())
                .addBytes(processName, bpmnBytes)
                .deploy();

        System.out.println("流程部署成功：" + deploy.getId());
    }


    @Test
    public void deploy000() {

        // 部署流程
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId("2501").singleResult();

        System.out.println("流程部署成功：" + processDefinition.getId());
    }



    /**
     * 启动流程
     * <p>
     * deployId 部署的流程 Id，来自 ACT_RE_PROCDEF
     * userId   用户 Id
     * dataKey  数据 Key，业务键，一般为表单数据的 ID，仅作为表单数据与流程实例关联的依据
     */
    @Test
    public void start() {
        String userId = "yy";
        String deployId = "test-group:1:4";
        String dataKey = "id-123";
        // 设置发起人
        identityService.setAuthenticatedUserId(userId);
        // 根据流程 ID 启动流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(deployId, dataKey);
        System.out.println("流程启动成功：" + deployId);
        System.out.println("流程实例ID：" + processInstance.getProcessInstanceId());
    }

    /**
     * 获取当前候选组
     * <p>
     * taskId 任务 Id，来自 ACT_RU_TASK
     */
    @Test
    public void taskInfo() {

        String taskId = "2507";

        List<String> group = new ArrayList<>();
        List<String> user = new ArrayList<>();
        List<IdentityLink> taskName = taskService.getIdentityLinksForTask(taskId);
        taskName.forEach(identityLink -> {
            group.add(identityLink.getGroupId());
            user.add(identityLink.getUserId());
        });

        System.out.println(JSONUtil.toJsonPrettyStr(group));
        System.out.println(JSONUtil.toJsonPrettyStr(user));
    }

    /**
     * 获取正在运行的数据 Id 列表
     */
    @Test
    public void getRuntimeDataId() {
        List<String> idList = new ArrayList<>();
        // 获取正在执行的任务列表
        List<Execution> list = runtimeService.createExecutionQuery().onlyProcessInstanceExecutions().list();
        list.forEach(execution -> {
            // 根据任务获取流程实例
            // 获取流程实例种的业务 key
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(execution.getProcessInstanceId()).singleResult();
            idList.add(pi.getBusinessKey());
        });
        System.out.println(JSONUtil.toJsonPrettyStr(idList));
    }

    /**
     * 根据用户，获取需要审核的业务键 business_key 列表
     * <p>
     * userId 用户 Id
     */
    @Test
    public void getRuntimeBusinessKeyByUser() {
        String userId = "wt";

        List<Map<String, Object>> idList = new ArrayList<>();
        // 根据用户获取正在进行的任务
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).list();
        tasks.forEach(task -> {
            Map<String, Object> data = new HashMap<>();
            // 根据任务获取流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            // 获取流程实例中的业务键
            data.put("businessKey", processInstance.getBusinessKey());
            // 获取任务 Id
            data.put("taskId", task.getId());
            // 流程定义名称
            data.put("processInstanceName", processInstance.getProcessDefinitionName());
            // 流程开始时间
            data.put("startTime", processInstance.getStartTime());
            idList.add(data);
        });

        System.out.println(JSONUtil.toJsonPrettyStr(idList));
    }

    /**
     * 根据任务节点获取流程实例 Id
     * <p>
     * 表：act_ru_task
     */
    @Test
    public void getProcessInstanceId() {
        String taskId = "10005";
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        System.out.println(task.getProcessInstanceId());
    }

    /**
     * 根据候选人获取任务列表
     */
    @Test
    public void getRuntimeBusinessKeyByCandidateUser() {
        String userId = "wt";

        List<Map<String, Object>> idList = new ArrayList<>();
        // 根据用户获取正在进行的任务
        List<Task> tasks = taskService.createTaskQuery().taskCandidateUser(userId).list();
        tasks.forEach(task -> {
            Map<String, Object> data = new HashMap<>();
            // 根据任务获取流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            // 获取流程实例中的业务键
            data.put("businessKey", processInstance.getBusinessKey());
            // 获取任务 Id
            data.put("taskId", task.getId());
            // 流程定义名称
            data.put("processInstanceName", processInstance.getProcessDefinitionName());
            // 流程开始时间
            data.put("startTime", processInstance.getStartTime());
            idList.add(data);
        });

        System.out.println(JSONUtil.toJsonPrettyStr(idList));
    }

    /**
     * 领取任务
     */
    @Test
    public void claimTask() {
        String userId = "lpf222";
        String taskId = "2507";
        //taskService.claim(taskId,userId);
        taskService.setAssignee(taskId, userId);
    }

    /**
     * 完成任务
     */
    @Test
    public void completeTask() {
        String taskId = "15003";
        Map<String, Object> variables = new HashMap<>();
        variables.put("key3", "value3");
        variables.put("key4", "value4");
        variables.put("tongguo", "tongguo");

        // 添加批注信息
        taskService.addComment(taskId, null, "备注信息");
        taskService.complete(taskId, variables);
    }

    /**
     * act_ru_actinst：存储运行时节点信息
     */
    @Test
    public void getActivity() {

        String processInstanceId = "2501";

        // 获取当前活动节点列表
        List<String> finalActiveActivityIds01 = runtimeService.getActiveActivityIds(processInstanceId);
        System.out.println(JSONUtil.toJsonPrettyStr(finalActiveActivityIds01));

        // 获取连线的列表
        List<ActivityInstance> highLightedFlowInstances = runtimeService
                .createActivityInstanceQuery()
                .activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)
                .processInstanceId(processInstanceId)
                .list();
        List<String> finalActiveActivityIds02 = new ArrayList<>();
        highLightedFlowInstances.forEach(activityInstance -> finalActiveActivityIds02.add(activityInstance.getActivityId()));
        System.out.println(JSONUtil.toJsonPrettyStr(finalActiveActivityIds02));

        // 获取用户任务线的列表
        highLightedFlowInstances = runtimeService
                .createActivityInstanceQuery()
                .activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
                .processInstanceId(processInstanceId)
                .list();
        List<String> finalActiveActivityIds03 = new ArrayList<>();
        highLightedFlowInstances.forEach(activityInstance -> finalActiveActivityIds03.add(activityInstance.getActivityId()));
        System.out.println(JSONUtil.toJsonPrettyStr(finalActiveActivityIds03));
    }

    //------------------------------------------------------------------------------------------------------------------

    /**
     * 普通串行路线上的退回（此流程中没有并行网关的退回时），此方法支持普通串行节点/会签多实例节点/排他网关节点
     */
    @Test
    public void tuiHuiTaskOfPuTong() {

        String proInstanceId = "2501";
        String currentActivityId = "group";
        String newActivityId = "users";

        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(proInstanceId)
                .moveActivityIdTo(currentActivityId, newActivityId)
                .changeState();
    }

    /**
     * 网管的退回
     */
    @Test
    public void tuiHuiTaskOfBingXing() {

        String proInstanceId = "2501";
        String targetTaskKey = "2501";

        // 并行网关的退回
        List<String> currentExecutionIds = new ArrayList<>();
        List<Execution> executions = runtimeService.createExecutionQuery().parentId(proInstanceId).list();
        for (Execution execution : executions) {
            System.out.println("并行网关节点数：" + execution.getActivityId());
            currentExecutionIds.add(execution.getId());
        }
        runtimeService.createChangeActivityStateBuilder()
                .moveExecutionsToSingleActivityId(currentExecutionIds, targetTaskKey)
                .changeState();
    }

    //------------------------------------------------------------------------------------------------------------------

    /**
     * 获取用户审核历史
     * <p>
     * userId 发起人 Id
     */
    @Test
    public void getHistoryByUser() {

        String userId = "lpf222";

        List<Map<String, Object>> historyList = new ArrayList<>();
        // 根据用户，查询任务实例历史
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished().orderByHistoricTaskInstanceEndTime().desc().list();
        list.forEach(historicTaskInstance -> {
            // 历史流程实例
            HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
            // 获取需要的历史数据
            Map<String, Object> historyInfo = new HashMap<>();
            historyInfo.put("assignee", historicTaskInstance.getAssignee());
            // 节点名称
            historyInfo.put("nodeName", historicTaskInstance.getName());
            // 流程开始时间
            historyInfo.put("startTime", historicTaskInstance.getCreateTime());
            // 节点操作时间（本流程节点结束时间）
            historyInfo.put("endTime", historicTaskInstance.getEndTime());
            // 流程定义名称
            historyInfo.put("processName", hpi.getProcessDefinitionName());
            // 流程实例 ID
            historyInfo.put("processInstanceId", historicTaskInstance.getProcessInstanceId());
            // 业务键
            historyInfo.put("businessKey", hpi.getBusinessKey());
            historyList.add(historyInfo);
        });

        System.out.println(JSONUtil.toJsonPrettyStr(historyList));
    }


    /**
     * 查询历史流程实例
     */
    @Test
    public void findHistoryProcessInstance() {
        String processInstanceId = "2501";
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByProcessInstanceStartTime()
                .asc()
                .singleResult();
        System.out.println(JSONUtil.toJsonPrettyStr(hpi));
    }

    /**
     * 查询历史活动
     */
    @Test
    public void findHistoryActivity() {
        String processInstanceId = "2501";
        List<HistoricActivityInstance> activityInstances = historyService
                .createHistoricActivityInstanceQuery()
                .activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();

        List<Map<String, Object>> list = new ArrayList<>();
        activityInstances.forEach(activityInstance -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", activityInstance.getId());
            map.put("processInstanceId", activityInstance.getProcessInstanceId());
            map.put("activityType", activityInstance.getActivityType());
            map.put("startTime", activityInstance.getStartTime());
            map.put("endTime", activityInstance.getEndTime());
            map.put("durationInMillis", activityInstance.getDurationInMillis());
            map.put("assignee", activityInstance.getAssignee());
            map.put("activityId", activityInstance.getActivityId());
            map.put("activityName", activityInstance.getActivityName());

            list.add(map);
        });

        System.out.println(JSONUtil.toJsonPrettyStr(list));
    }

    /**
     * 查询历史任务
     */
    @Test
    public void findHistoryTask() {
        String processInstanceId = "2101";
        List<HistoricTaskInstance> list = processEngine.getHistoryService()// 与历史数据（历史表）相关的Service
                .createHistoricTaskInstanceQuery()// 创建历史任务实例查询
                .processInstanceId(processInstanceId)//
                .orderByHistoricTaskInstanceStartTime().asc().list();
        if (list != null && list.size() > 0) {
            for (HistoricTaskInstance hti : list) {
                System.out.println(hti.getId() + "    " + hti.getName() + "    " + hti.getProcessInstanceId() + "   "
                        + hti.getStartTime() + "   " + hti.getEndTime() + "   " + hti.getDurationInMillis());
                System.out.println("################################");
            }
        }
    }

    /**
     * 查询历史流程变量
     */
    @Test
    public void findHistoryProcessVariables() {
        String processInstanceId = "2101";
        List<HistoricVariableInstance> list = processEngine.getHistoryService()//
                .createHistoricVariableInstanceQuery()// 创建一个历史的流程变量查询对象
                .processInstanceId(processInstanceId)//
                .list();
        if (list != null && list.size() > 0) {
            for (HistoricVariableInstance hvi : list) {
                System.out.println(hvi.getId() + "   " + hvi.getProcessInstanceId() + "   " + hvi.getVariableName()
                        + "   " + hvi.getVariableTypeName() + "    " + hvi.getValue());
                System.out.println("###############################################");
            }
        }
    }

    //------------------------------------------------------------------------------------------------------------------

    /**
     * 获取批注内容
     */
    @Test
    public void getProcessComments() {

        String processInstanceId = "2501";

        List<Comment> historyCommnets = new ArrayList<>();
        // 1) 获取流程实例的ID

        // 2）通过流程实例查询所有的(用户任务类型)历史活动
        List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                //.activityType(BpmnXMLConstants.ATTRIBUTE_LISTENER_EVENT)
                .list();

        // 3）查询每个历史任务的批注
        for (HistoricActivityInstance activityInstance : activityInstances) {
            String historytaskId = activityInstance.getTaskId();
            List<Comment> comments = taskService.getTaskComments(historytaskId);
            // 4）如果当前任务有批注信息，添加到集合中
            if (comments != null && comments.size() > 0) {
                historyCommnets.addAll(comments);
            }
        }
        // 5）返回
        System.out.println(JSONUtil.toJsonPrettyStr(historyCommnets));
    }

    @Test
    public void findCommentByTaskId() {

        // 历史的任务id
        String taskId = "2507";

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        //使用流程实例ID，查询历史任务，获取历史任务对应的每个任务ID
        List<HistoricTaskInstance> historicTaskInstances = historyService
                .createHistoricTaskInstanceQuery()
                .taskId(taskId)
                .list();

        //遍历集合，获取每个任务ID
        List<Comment> comments = new ArrayList<>();
        historicTaskInstances.forEach(historicTaskInstance -> {
            //获取批注信息
            List<Comment> taskComments = taskService.getTaskComments(historicTaskInstance.getId(), "event");
            comments.addAll(taskComments);
        });
        System.out.println(JSONUtil.toJsonPrettyStr(comments));

        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(historicTaskInstances.get(0).getProcessInstanceId());
        System.out.println(JSONUtil.toJsonPrettyStr(processInstanceComments));
    }

    public void sss() {

    }
}
